package com.outdd.vaietool.common.utils;

import com.outdd.vaietool.common.constants.Constant;
import com.outdd.vaietool.common.constants.RedisKeyConstantFun;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.*;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.Lock;

/**
 * @program: ReptileUtil
 * @description:
 * @author: VAIE
 * @create: 2021-05-07 11:19
 **/
@Slf4j
public class ReptileUtil {



    public static HttpUtils httpUtils = HttpUtils.getInstance();

    /**
     * 根据url获取Document
     *
     * @param url
     * @param lock lock锁
     * @return: org.jsoup.nodes.Document
     * @auther: vaie
     * @date: 2018/11/2 22:16
     */
    public static Document getDocumentOfHttps(String url, Lock lock) {

        Document document = null;
        lock.lock();//获取写锁
        try {
            document = httpUtils.executeGetWithSSLAsDocument(url);
        } catch (Exception e) {
           log.error("getDocumentOfHttps error",e);
        } finally {
            lock.unlock();//释放写锁
        }
        return document;
    }

    /**
     * 获取Document通过HTTPS
     *
     * @param url 网络地址
     * @return: org.jsoup.nodes.Document
     * @auther: vaie
     * @date: 2018/11/2 21:10
     */
    public static Document getDocumentOfHttps(String url) {
        Document document = null;
        try {
            document = Jsoup.connect(url)
                    .userAgent("Mozilla")
                    .cookie("auth", "token")
                    .timeout(9000)
                    .get();
        } catch (Exception e) {
            log.error("getDocumentOfHttps error",e);
        }
        return document;
    }

    /**
     * 获取Document通过HTTPS
     *
     * @param url 网络地址
     * @return: org.jsoup.nodes.Document
     * @auther: vaie
     * @date: 2018/11/2 21:10
     */
    public static Document getDocumentOfPost(String url,Map <String,String> map,String cookie) {
        Document document = null;
        try {
            Connection connect = Jsoup.connect(url);
            //遍历生成参数
            if(map!=null){
                for (Map.Entry<String, String> entry : map.entrySet()) {
                    //添加参数
                    connect.data(entry.getKey(), entry.getValue());
                }
            }
            document =
                    connect
                    .header("Cookie", cookie)
                    .header("Content-Type", "application/x-www-form-urlencoded")
                    .timeout(9000)
                    .post();
        } catch (Exception e) {
            log.error("getDocumentOfHttps error",e);
        }
        return document;
    }

    /**
     * 获取域名
     *
     * @param url
     * @return: java.lang.String
     * @auther: bjxdts
     * @date: 2018/10/31 15:07
     */
    public static String getHost(String url) throws MalformedURLException {

        java.net.URL Url = new java.net.URL(url);
        // 获取主机名;
        String host = Constant.HTTPS + Url.getHost();
        return host;
    }

    /**
     * 获取url
     * @param doc
     * @return
     */
    public static String getUrl(Document doc){
        String utl = "";
        Elements elements = doc.select("a");
        if (ObjectUtils.isNotEmpty(elements)) {
            Element element = elements.get(0);
            if (ObjectUtils.isNotEmpty(element)) {
                utl = element.absUrl("href");
            }
        }
        return  utl;
    }

    /**
     * 查询是否有下一章
     * @param doc
     * @param nextName 下一章的规则id民或者class名
     * @return: String url - boolen filag 需要强制转换
     * @auther: bjxdts
     * @date: 2018/10/31 14:52
     */
    public static Map<String, Object> isNext(Document doc, String nextName){
        Map<String, Object> map = new HashMap<String, Object>(1);
        boolean filag = true;
        Elements next = doc.select(nextName);
        if (next.size() > 0) {
            map.put("url", next.get(0).attr("href"));
        } else {
            filag = false;
        }
        map.put("filag", filag);
        return map;
    }

    /**
     * 获取小说内容
     * @param doc Document
     * @param titleRule 标题规则
     * @param contentsRule 内容规则
     * @return: java.lang.String
     * @auther: vaie
     * @date: 2018/11/2 20:58
     */
    public static String getDetails(Document doc,String titleRule,String contentsRule){
        StringBuffer resultContent = new StringBuffer();
        String text = doc.text();
        //标题
        Elements titles = doc.select(titleRule);
        //内容
        Elements contents = doc.select(contentsRule);
        for (Element title : titles) {
            resultContent.append(
                    Constant.CRLF
                    + title.text()
                    + Constant.CRLF
                    + Constant.CRLF);
        }
        for (Element content : contents) {
            resultContent.append(
                    content.text().replaceAll(" ", Constant.CRLF)
                            + Constant.CRLF);
        }
        return resultContent.toString();
    }

    /**
     * 获取小说内容(听书)
     * @param doc Document
     * @param titleRule 标题规则
     * @param contentsRule 内容规则
     * @return: java.lang.String
     * @auther: vaie
     * @date: 2018/11/2 20:58
     */
    public static String getDetailsts(Document doc,String titleRule,String contentsRule){
        StringBuffer resultContent = new StringBuffer();
        //标题
        Elements titles = doc.select(titleRule);
        //内容
        Elements contents = doc.select(contentsRule);

        String vUrl = "";
        String vContent = "";
        for (Element title : titles) {
            vUrl = title.text();
            resultContent.append(vUrl + Constant.CRLF);
        }
        for (Element content : contents) {
            vContent = content.attr("src").replaceAll(" ", Constant.CRLF);
            resultContent.append(vContent + Constant.CRLF);
        }
        try{
            ReptileUtil.downLoadFromUrl(vContent,
                    vUrl+".mp3","D:/shengxu/");
        }catch (Exception e) {
            log.error("getDetailsts",e);
        }
        return resultContent.toString();
    }

    /**
     * 爬取小说内容信息
     *
     * @param doc Document类
     * @return: NovelVolume 内容类
     * @auther: vaie
     * @date: 2018/11/9 22:40
     */
    public static String getContent(Document doc,String rule) {
        String content="";
        if (ObjectUtils.isNotEmpty(doc)) {
            try {
                //内容
                Element contents = doc.select(rule).get(0);
                content=(contents.text().replaceAll(" ", Constant.CRLF) + Constant.CRLF);
            }catch (Exception e){
                log.error("getContent error",e);
            }

        }
        return content;
    }
    /**
     * 爬取小说内容信息
     *
     * @param url Document类
     * @return: NovelVolume 内容类
     * @auther: vaie
     * @date: 2018/11/9 22:40
     */
    public static String getContent(String  url,String rule) {

        return getContent(ReptileUtil.getDocumentOfHttps(url),rule);
    }

    /**
     * 通过地址获取目录标题和地址
     * @param url 网络地址
     * @param titleRule 标题规则
     * @return: java.util.List<java.util.Map<java.lang.String,java.lang.String>>
     * @auther: vaie
     * @date: 2018/11/3 17:59
     */
    public static List<Map<String,String>> getNovelByUrlToList(String url, String titleRule){
        Document doc = getDocumentOfHttps(url);
        List<Map<String, String>> list = new ArrayList<Map<String, String>>();
        if (ObjectUtils.isNotEmpty(doc)) {
            //标题
            Elements titleUrls = doc.select(titleRule);
            for (Element titleUrl : titleUrls) {
                Map<String,String> map = new HashMap<String,String>();
                map.put("title", titleUrl.text());
                map.put("url", "https:" + titleUrl.attr("href"));
                list.add(map);
            }
        }
        return list;
    }

    /**
     * 通过地址获取目录标题和地址
     * @return: java.util.List<java.util.Map<java.lang.String,java.lang.String>>
     * @auther: vaie
     * @date: 2018/11/3 17:59
     */
    public static List<Map<String,String>> getNovelByUrlToList(String url){
        Document doc = getDocumentOfHttps(url);
        List<Map<String, String>> list = new ArrayList<Map<String, String>>();
        if (ObjectUtils.isNotEmpty(doc)) {
            //li
            Elements lis = doc.select(".res-book-item");
            for (Element li : lis) {
                Map<String,String> map = new HashMap<String,String>();
                //div
                Elements infos = li.select(".book-mid-info");
                for(Element info : infos){
                    Elements urlAndTitles =info.select("h4 a");
                    for(Element urlAndTitle : urlAndTitles){
                        map.put("title", urlAndTitle.text());
                        map.put("url", "https:" + urlAndTitle.attr("href"));
                    }
                    Elements authors =info.select(".author");
                    for(Element author : authors){
                        Elements names =info.select(".name");
                        for(Element name : names){
                            map.put("aout", name.text());
                        }
                    }
                }
                list.add(map);
            }
        }
        return list;
    }

    /**
     * 通过已解析的网页文件获取目录标题和地址
     * @param doc 已解析的网页文件
     * @param rule 规则
     * @return: java.util.List<java.util.Map<java.lang.String,java.lang.String>>
     * @auther: vaie
     * @date: 2018/11/3 17:59
     */
    public static List<Map<String,String>> getNovelByUrlToList(Document doc,String rule){
        List<Map<String, String>> list = new ArrayList<Map<String, String>>();
        if (ObjectUtils.isNotEmpty(doc)) {
            //标题
            Elements titleUrls = doc.select(rule);

            for (Element titleUrl : titleUrls) {
                Map<String,String> map = new HashMap<String,String>();
                map.put("title", titleUrl.text());
                map.put("url", titleUrl.absUrl("href"));
                list.add(map);
            }
        }
        return list;
    }

    /**
     * 获取全部目录转为Document
     * @param url 网址
     * @param directoryRule 规则
     * @return: java.util.List<org.jsoup.nodes.Document>
     * @auther: vaie
     * @date: 2018/11/4 17:21
     */
    public static List<Document> getDirectoryAll(String url,String directoryRule,Boolean isAddHost,Integer skipNum,String uuid) {
        Document doc = ReptileUtil.getDocumentOfHttps(url);

        List<Document> fdList = null;
        if (ObjectUtils.isNotEmpty(doc)) {
            //标题
            Elements titleUrls = doc.select(directoryRule);
            fdList = new ArrayList<Document>();
            String host= null;
            try {
                host = getHost(url);
            } catch (MalformedURLException e) {
                e.printStackTrace();
            }
            int i = 0;
            int size = titleUrls.size();
            RedisUtil.set(RedisKeyConstantFun.getDownProgressARTICLE(uuid),"0");
            for (Element titleUrl : titleUrls) {

                BigDecimal value = new BigDecimal(i+1).divide(new BigDecimal(size),10, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).setScale(2,BigDecimal.ROUND_HALF_UP);
                RedisUtil.set(RedisKeyConstantFun.getDownProgressARTICLE(uuid),value.toString());
                i++;
                if (skipNum>=i) {
                    continue;
                }

                String urll = "";
                if (isAddHost) {
                    urll=host+titleUrl.attr("href");
                }else{
                    urll="https:"+titleUrl.attr("href");
                }

                Document document = ReptileUtil.getDocumentOfHttps(urll);
                if(ObjectUtils.isNotEmpty(document)){
                    fdList.add(document);
                }
            }
        }
        return fdList;
    }

    /**
     * 把Document转换为text
     * @param fdList
     * @param titleRule 标题规则
     * @param contentsRule 内容规则
     * @return: void
     * @auther: vaie
     * @date: 2018/11/4 17:37
     */
    public static String DocumentToTxt(List<Document> fdList,String titleRule,String contentsRule,String uuid){
        int size = fdList.size();
        RedisUtil.set(RedisKeyConstantFun.getDownProgressARTICLE(uuid),"0");
        StringBuilder Details=new StringBuilder();
        for (int i = 0; i <fdList.size() ; i++) {
            Document doc = fdList.get(i);
            BigDecimal value = new BigDecimal(i+1).divide(new BigDecimal(size),10, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100)).setScale(2,BigDecimal.ROUND_HALF_UP);
            RedisUtil.set(RedisKeyConstantFun.getDownProgressARTICLE(uuid),value.toString());
            if(ObjectUtils.isNotEmpty(doc)){
                Details.append(ReptileUtil.getDetails(doc,titleRule,contentsRule));
            }
        }

        RedisUtil.set(RedisKeyConstantFun.getDownProgressARTICLE(uuid),"ok");
        return Details.toString();
    }

    /**
     * 从网络Url中下载文件
     * @param urlStr
     * @param fileName
     * @param savePath
     * @throws IOException
     */
    public static void  downLoadFromUrl(String urlStr,String fileName,String savePath) throws IOException{
        URL url = new URL(urlStr);
        HttpURLConnection conn = (HttpURLConnection)url.openConnection();
        //设置超时间为3秒
        conn.setConnectTimeout(3*1000);
        //防止屏蔽程序抓取而返回403错误
        conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

        //得到输入流
        InputStream inputStream = conn.getInputStream();
        //获取自己数组
        byte[] getData = readInputStream(inputStream);

        //文件保存位置
        File saveDir = new File(savePath);
        if(!saveDir.exists()){
            saveDir.mkdir();
        }
        File file = new File(saveDir+File.separator+fileName);
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(getData);
        if(fos!=null){
            fos.close();
        }
        if(inputStream!=null){
            inputStream.close();
        }
    }





    /**
     * 从输入流中获取字节数组
     * @param inputStream
     * @return
     * @throws IOException
     */
    public static  byte[] readInputStream(InputStream inputStream) throws IOException {
        byte[] buffer = new byte[1024];
        int len = 0;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        while((len = inputStream.read(buffer)) != -1) {
            bos.write(buffer, 0, len);
        }
        bos.close();
        return bos.toByteArray();
    }


    public static Element parseElements(Elements el,String rule,int index){
        Elements els =el.select(rule);
       return els.get(index);
    }

    public static String parseElementsText(Elements el,String rule){
       return parseElements(el,rule,0).text();
    }

    public static String parseElementsText(Elements el,String rule ,int index){
       return parseElements(el,rule,index).text();
    }


    public static Element parseElement(Element el,String rule,int index){
        Elements els =el.select(rule);
       return els.get(index);
    }

    public static String parseElementText(Element el,String rule){
       return parseElement(el,rule,0).text();
    }

    public static String parseElementText(Element el,String rule ,int index){
       return parseElement(el,rule,index).text();
    }

}
